package com.starter.es.config;

import com.starter.es.properties.ElasticSearchProperties;
import com.starter.es.service.AggsService;
import com.starter.es.service.IndexService;
import com.starter.es.service.SearchService;
import com.starter.es.service.SimpleIndexService;
import com.starter.es.service.SimpleSearchService;
import com.starter.es.service.impl.AggsServiceImpl;
import com.starter.es.service.impl.IndexServiceImpl;
import com.starter.es.service.impl.SearchServiceImpl;
import com.starter.es.service.impl.SimpleIndexServiceImpl;
import com.starter.es.service.impl.SimpleSearchServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.elasticsearch.client.RestClientBuilder.RequestConfigCallback;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties
@Slf4j
public class ElasticSearchAutoConfiguration {

  public static int CONNECT_TIMEOUT_MILLIS = 1000;
  public static int SOCKET_TIMEOUT_MILLIS = 30000;
  public static int CONNECTION_REQUEST_TIMEOUT_MILLIS = 500;
  public static int MAX_CONN_PER_ROUTE = 10;
  public static int MAX_CONN_TOTAL = 30;

  @Autowired
  private ElasticSearchProperties elasticSearchProperties;

  @Bean
  public ElasticSearchProperties elasticSearchProperties() {
    log.info("boot.spring.config.ElasticSearchPropertiesAutoConfiguration.elasticSearchProperties");
    return new ElasticSearchProperties();
  }

  @Bean
  public RestHighLevelClient restHighLevelClient() {
    log.info(
        "boot.spring.config.ElasticSearchPropertiesAutoConfiguration.configRestHighLevelClient, elasticSearchProperties:[ {} ]",
        elasticSearchProperties);
    final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    //访问用户名和密码,阿里云Elasticsearch实例时设置的用户名和密码，也是Kibana控制台的登录用户名和密码。
    credentialsProvider.setCredentials(
        AuthScope.ANY, new UsernamePasswordCredentials(elasticSearchProperties.getUsername(),
            elasticSearchProperties.getPassword()));
    // 通过builder创建rest client，配置http client的HttpClientConfigCallback。
    // 单击所创建的Elasticsearch实例ID，在基本信息页面获取公网地址，即为ES集群地址。
    RestClientBuilder builder = RestClient
        .builder(new HttpHost(elasticSearchProperties.getUris(), elasticSearchProperties.getPort()))
        /**
         * 配置连接时间延时
         */
        .setRequestConfigCallback(new RequestConfigCallback() {
          @Override
          public Builder customizeRequestConfig(Builder requestConfigBuilder) {
            requestConfigBuilder.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
            requestConfigBuilder.setSocketTimeout(SOCKET_TIMEOUT_MILLIS);
            requestConfigBuilder.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT_MILLIS);
            return requestConfigBuilder;
          }
        })
        /**
         * 使用异步httpclient时设置并发连接数
         */
        .setHttpClientConfigCallback(new HttpClientConfigCallback() {

          @Override
          public HttpAsyncClientBuilder customizeHttpClient(
              HttpAsyncClientBuilder httpClientBuilder) {
            httpClientBuilder.setMaxConnTotal(MAX_CONN_TOTAL);
            httpClientBuilder.setMaxConnPerRoute(MAX_CONN_PER_ROUTE);
            return httpClientBuilder;
          }
        })
        .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
          @Override
          public HttpAsyncClientBuilder customizeHttpClient(
              HttpAsyncClientBuilder httpClientBuilder) {
            return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
          }
        });
    // RestHighLevelClient实例通过REST low-level client builder进行构造。
    RestHighLevelClient client = new RestHighLevelClient(builder);

    return client;
  }

  @Bean
  public IndexService indexService(RestHighLevelClient restHighLevelClient) {
    log.info(
        "boot.spring.config.ElasticSearchPropertiesAutoConfiguration.indexService, configRestHighLevelClient:[ {} ]",
        restHighLevelClient);
    return new IndexServiceImpl(restHighLevelClient);
  }

  @Bean
  public AggsService aggsService(RestHighLevelClient restHighLevelClient) {
    log.info(
        "boot.spring.config.ElasticSearchPropertiesAutoConfiguration.aggsService, configRestHighLevelClient:[ {} ]",
        restHighLevelClient);
    return new AggsServiceImpl(restHighLevelClient);
  }

  @Bean
  public SearchService searchService(RestHighLevelClient restHighLevelClient) {
    log.info(
        "boot.spring.config.ElasticSearchPropertiesAutoConfiguration.searchService, configRestHighLevelClient:[ {} ]",
        restHighLevelClient);
    return new SearchServiceImpl(restHighLevelClient);
  }

  @Bean
  public SimpleSearchService simpleSearchService(RestHighLevelClient restHighLevelClient) {
    log.info(
        "boot.spring.config.ElasticSearchPropertiesAutoConfiguration.simpleSearchService, configRestHighLevelClient:[ {} ]",
        restHighLevelClient);
    return new SimpleSearchServiceImpl(restHighLevelClient);
  }

  @Bean
  public SimpleIndexService simpleIndexService(RestHighLevelClient restHighLevelClient) {
    log.info(
        "boot.spring.config.ElasticSearchPropertiesAutoConfiguration.simpleIndexService, configRestHighLevelClient:[ {} ]",
        restHighLevelClient);
    return new SimpleIndexServiceImpl(restHighLevelClient);
  }
}
